package web

import (
	"fmt"
	"gitee.com/then-go-project/go-utils/base"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"io"
	"mime/multipart"
	"net/http"
	"reflect"
)

type BizErr struct {
	Msg string
}

func (b BizErr) Error() string {
	return b.Msg
}

type ReqAttr struct {
	CtxMap  map[string]interface{}
	Headers http.Header
}

type RestHandler interface {
	Execute(attr *ReqAttr) (interface{}, error)
}

type FileHandler interface {
	Execute(form *multipart.Form, attr *ReqAttr) (interface{}, error)
}

func RestRouting(r RestHandler) func(*gin.Context) {
	return func(context *gin.Context) {
		//创建新对象
		paramType := reflect.TypeOf(r).Elem()
		newObj := reflect.New(paramType).Interface()

		if context.Request.Method == http.MethodGet {
			if err := context.ShouldBindQuery(newObj); err != nil && dealReqErr(context, err, paramType, newObj) {
				return
			}
		} else if context.Request.Method == http.MethodPost {
			if err := context.ShouldBindJSON(newObj); err != nil && dealReqErr(context, err, paramType, newObj) {
				return
			}
		} else {
			RespBizFail(context, "不支持该请求方式！")
			return
		}
		r := newObj.(RestHandler)

		execute, err := r.Execute(generateReqAttr(context))
		if err != nil {
			base.ZapLog.Error("业务处理失败", zap.Error(err))
			switch err.(type) {
			case BizErr:
				RespBizFail(context, err.Error())
				recordLog(context, newObj, err.Error(), false)
				return
			default:
				recordLog(context, newObj, err.Error(), false)

				panic(err)
			}
		}
		recordLog(context, newObj, execute, true)

		RespSuccess(context, execute)
	}
}

func dealReqErr(context *gin.Context, err error, paramType reflect.Type, paramValue interface{}) bool {
	if paramType.NumField() == 0 {
		return false
	}

	base.ZapLog.Error("请求错误", zap.Error(err))
	if err == io.EOF {
		RespBizFail(context, "请求参数不能为空")
		recordLog(context, nil, "请求参数不能为空", false)
		return true
	}

	errMsg := FormatError(err, paramType)
	RespBizFail(context, errMsg)

	recordLog(context, paramValue, errMsg, false)
	return true
}

func FileRouting(f FileHandler) func(*gin.Context) {
	return func(context *gin.Context) {
		if context.Request.Method != http.MethodPost {
			RespBizFail(context, "请求方式错误！")
			return
		}
		form, err := context.MultipartForm()
		if err != nil {
			base.ZapLog.Error("获取附件失败", zap.Error(err))
			RespBizFail(context, "获取附件失败！")

			recordLog(context, nil, "获取附件失败", false)
			return
		}
		execute, err := f.Execute(form, generateReqAttr(context))
		if err != nil {
			base.ZapLog.Error("业务处理失败", zap.Error(err))
			switch err.(type) {
			case BizErr:
				RespBizFail(context, err.Error())
				recordLog(context, formatMultipartForm(form), err.Error(), false)
				return
			default:
				recordLog(context, formatMultipartForm(form), err.Error(), false)
				panic(err)
			}
		}
		recordLog(context, formatMultipartForm(form), execute, true)
		RespSuccess(context, execute)
	}
}

func formatMultipartForm(form *multipart.Form) interface{} {
	paramsMap := form.Value
	for key, val := range form.File {
		paramsMap[key] = append(paramsMap[key], fmt.Sprintf("%d", len(val)))
	}
	return paramsMap
}

func generateReqAttr(context *gin.Context) *ReqAttr {
	return &ReqAttr{
		CtxMap:  context.Keys,
		Headers: context.Request.Header,
	}
}

func recordLog(context *gin.Context, req, res interface{}, success bool) {
	if rl != nil {
		go rl.RecordLog(context.Request.URL.Path, req, res, success, context.Request.Header)
	}
}
